using System;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.Data;
using System.IO;

namespace XmlToHtml
{
    #region FontStyle

    public class FontStyle
	{
		public string Style = null;
		public FontStyle() {}
		public FontStyle(string Style)
		{
			this.Style = Style;
		}
		public string BeginTag
		{
			get
			{
				return "<font" +
					(string)((Style != null) ? " " + Style + ">" : ">");
			}
		}
		public string EndTag
		{
			get
			{
				return "</font>";
			}
		}
    }

    #endregion

    #region PageStyle

    public class PageStyle
	{
		public FontStyle Font = new FontStyle("color=\"black\" face=\"Courier New\" size=\"2\"");
		public string Style = null;
		public PageStyle() {}
		public PageStyle(string Style)
		{
			this.Style = Style;
		}
		public string BeginTag
		{
			get
			{
				return "<table" +
					(string)((Style != null) ? " " + Style + ">" : ">") +
					"<tr><td><pre>" + Font.BeginTag;
			}
		}
		public string EndTag
		{
			get
			{
				return Font.EndTag + "</pre></td></tr></table>";
			}
		}
    }

    #endregion

    #region CodeToHTML

    public class CodeToHTML
    {
        #region Formatting

        public class CodeStyle
		{
			public PageStyle Page = new PageStyle();
			public FontStyle Comment = new FontStyle("color=\"green\"");
			public FontStyle Keyword = new FontStyle("color=\"blue\"");
			public FontStyle XmlTag = new FontStyle("color=\"maroon\"");
			public CodeStyle() {}
        }

		public CodeStyle Style
		{
			get 
			{ 
				return codeStyle;
			}
			set
			{
				codeStyle = value;
			}
		}
		private int tabSize = 4;
		public int TabSize
		{
			get
			{
				return tabSize;
			}
			set
			{
				tabSize = value;
			}
		}
		public bool ShowFileName
		{
			get
			{
				return showFileName;
			}
			set
			{
				showFileName = value;
			}
		}
		public string Language
		{
			get
			{
				return language;
			}
			set
			{
				language = value;
			}
        }

        #endregion

        private bool showFileName = false;
        private string language = "";
        private CodeStyle codeStyle = new CodeStyle();


		private void SetLanguageFromFileName(string filePath)
		{
			// Find the current language from the file extension
			FileInfo info = new FileInfo(filePath);
		}
		public string RenderFile(string filePath)
		{
			// errors?
			this.SetLanguageFromFileName(filePath);
			return Render(File.OpenText(filePath));
		}
		public void RenderFile(string filePath, string outputFilePath)
		{
			// Render and throw error
			this.SetLanguageFromFileName(filePath);
			StreamWriter writer = new StreamWriter(outputFilePath);
			writer.Write(Render(File.OpenText(filePath)));
			writer.Flush();
			writer.Close();
		}
		public string RetabAndTrim(string inputString)
		{
			string tab = "";
			for(int i = 0; i < tabSize; i++)
			{
				tab += " ";
			}
			StringBuilder builder = new StringBuilder();
			StringWriter writer = new StringWriter(builder);
			// Split into a string array for processing
			inputString = inputString.Replace("\r\n", "\r");
			inputString = inputString.Replace("\n\r", "\r");
			string[] lines = inputString.Split(new char[]{'\r'});
			foreach(string line in lines)
			{
				string output = line;
				output = output.TrimEnd(new char[]{'\n','\r',' '});
				output = output.Replace(tab, "\t");
				output = output.TrimStart(new char[]{' '});
				writer.WriteLine(output);
			}
			writer.Flush();
			return writer.ToString();
		}
		public void RetabAndTrimFile(string filePath, string outputFilePath)
		{
			StreamReader reader = File.OpenText(filePath);
			string output = this.RetabAndTrim(reader.ReadToEnd());
			reader.Close();
			StreamWriter writer = new StreamWriter(outputFilePath);
			writer.Write(output);
			writer.Flush();
			writer.Close();
		}
		public string Render(StreamReader textReader)
		{
			return Render(textReader.ReadToEnd());
		}
        public string Render(string inputString)
        {
            string tab = "";
            for (int i = 0; i < tabSize; i++)
            {
                tab += " ";
            }
            StringBuilder builder = new StringBuilder();
            StringWriter writer = new StringWriter(builder);
            // Split into a string array for processing
            inputString = inputString.Replace("\r\n", "\r");
            inputString = inputString.Replace("\n\r", "\r");
            inputString = inputString.Replace("\t", tab);
            string[] lines = inputString.Split(new char[] { '\r' });

            // Process the language
            bool isInScriptBlock = false;
            bool isInMultiLine = false;
            writer.Write(codeStyle.Page.BeginTag);
            foreach (string line in lines)
            {
                if (this.IsScriptBlockTagStart(line))
                {
                    writer.WriteLine(this.FixASPXLine(line));
                    isInScriptBlock = true;
                }
                else
                {
                    if (this.IsScriptBlockTagEnd(line))
                    {
                        writer.WriteLine(this.FixASPXLine(line));
                        isInScriptBlock = false;
                    }
                    else
                    {
                        if (this.IsMultiLineTagStart(line) &
                            isInMultiLine == false)
                        {
                            writer.Write("<font color=blue><b>" +
                                HttpUtility.HtmlEncode(line));
                            isInMultiLine = true;
                        }
                        else
                        {
                            if (this.IsMultiLineTagEnd(line)
                                & isInMultiLine == true)
                            {
                                writer.Write(HttpUtility.HtmlEncode(line) +
                                    "</b></font>");
                                isInMultiLine = false;
                            }
                            else
                            {
                                if (isInMultiLine)
                                {
                                    writer.Write(HttpUtility.HtmlEncode(line));
                                }
                                else
                                {
                                    writer.WriteLine(this.FixASPXLine(line));
                                }
                            }
                        }
                    }
                }
            }
            writer.Write(codeStyle.Page.EndTag);
            // aspx-page sorted out


            writer.Flush();
            return writer.ToString();
        }
		private string FixASPXLine(string line)
		{
			string outLine = line;
			string searchExp = null;
			string replaceExp = null;
			if(line.Length == 0)
			{
				return line;
			}

			// Search for \t and replace it with 4 spaces            
			outLine = Regex.Replace(outLine, @"(?i)(\t)", " ");
			outLine = HttpUtility.HtmlEncode(outLine);

			// Single line comment or #include references.
			searchExp = "(?i)(?<a>(^.*))(?<b>(&lt;!--))(?<c>(.*))(?<d>(--&gt;))(?<e>(.*))";
			replaceExp = "${a}" + codeStyle.Comment.BeginTag + 
				"${b}${c}${d}" + codeStyle.Comment.EndTag + "${e}";
			if(Regex.IsMatch(outLine, searchExp))
			{
				outLine = Regex.Replace(outLine, searchExp, replaceExp);
			}

			// Colorize <%@ <type>
			searchExp = "(?i)" + "(?<a>(&lt;%@))" + "(?<b>(.*))" + "(?<c>(%&gt;))";
			replaceExp = "<font color=blue><b>${a}${b}${c}</b></font>";
			if(Regex.IsMatch(outLine, searchExp))
			{
				outLine = Regex.Replace(outLine, searchExp, replaceExp);
			}

			// Colorize <%# <type>
			searchExp = "(?i)" + "(?<a>(&lt;%#))" + "(?<b>(.*))" + "(?<c>(%&gt;))";
			replaceExp = "${a}" + "<font color=red><b>" + "${b}" + "</b></font>" + "${c}";
			if(Regex.IsMatch(outLine, searchExp))
			{
				outLine = Regex.Replace(outLine, searchExp, replaceExp);
			}

			// Colorize tag <type>
			searchExp =
				"(?i)" + 
				"(?<a>(&lt;)(?!%)(?!/?asp:)(?!/?template)(?!/?property)(?!/?ibuyspy:)(/|!)?)" +
				@"(?<b>[^;\s&]+)" + @"(?<c>(\s|&gt;|\Z))";
			replaceExp = "${a}" + 
				codeStyle.XmlTag.BeginTag + "${b}" + codeStyle.XmlTag.EndTag + "${c}";
			if(Regex.IsMatch(outLine, searchExp))
			{
				outLine = Regex.Replace(outLine, searchExp, replaceExp);
			}

			// Colorize asp:|template for runat=server tags <type>
			searchExp = "(?i)(?<a>&lt;/?)(?<b>(asp:|template|property|IBuySpy:).*)(?<c>&gt;)?";
			replaceExp = "${a}" + codeStyle.Keyword.BeginTag + "<b>${b}</b>" + 
				codeStyle.Keyword.EndTag + "${c}";
			if(Regex.IsMatch( outLine, searchExp))
			{
				outLine = Regex.Replace(outLine, searchExp, replaceExp);
			}

			// Colorize begin of tag char(s) "<","</","<%"
			searchExp = "(?i)(?<a>(&lt;)(/|!|%)?)";
			replaceExp = codeStyle.Keyword.BeginTag + "${a}" + codeStyle.Keyword.EndTag;
			if(Regex.IsMatch( outLine, searchExp))
			{
				outLine = Regex.Replace(outLine, searchExp, replaceExp);
			}

			// Colorize end of tag char(s) ">","/>"
			searchExp = "(?i)(?<a>(/|%)?(&gt;))";
			replaceExp = codeStyle.Keyword.BeginTag + "${a}" + codeStyle.Keyword.EndTag;
			if(Regex.IsMatch( outLine, searchExp))
			{
				outLine = Regex.Replace(outLine, searchExp, replaceExp);
			}
            outLine = outLine.Replace("xmlns", "<font color=red><b>xmlns</b></font>");                      

            return outLine;
		}
		private bool IsScriptBlockTagStart(string line)
		{
			bool returnCode = false;
			if(Regex.IsMatch(line, @"<script.*runat=""?server""?.*>"))
			{
				returnCode = true;
			}
			else
			{
				if(Regex.IsMatch(line, @"(?i)<%@\s*WebService"))
				{
					returnCode = true;
				}
			}
			return returnCode;
		}
		private bool IsScriptBlockTagEnd(string line)
		{
			bool returnCode = false;
			if(Regex.IsMatch(line, "</script.*>"))
			{
				returnCode = true;
			}
			return returnCode;
		}
		private bool IsMultiLineTagStart(string line)
		{
			bool returnCode = false;
			string outLine = null;
			string searchExp =
				"(?i)(?!.*&gt;)(?<a>&lt;/?)(?<b>(asp:|template|property|IBuySpy:).*)";
			outLine = HttpUtility.HtmlEncode( line );
			if(Regex.IsMatch(outLine, searchExp))
			{
				returnCode = true;
			}
			return returnCode;
		}
		private bool IsMultiLineTagEnd(string line)
		{
			bool returnCode = false;
			string outLine = null;
			string searchExp = "(?i)&gt;";
			outLine = HttpUtility.HtmlEncode(line);
			if(Regex.IsMatch( outLine, searchExp))
			{
				returnCode = true;
			}
			return returnCode;
		}

        public static void Test(string inputFilePath, string outputFilePath,
			int tabSize, CodeToHTML.CodeStyle style)
		{
			CodeToHTML converter = new CodeToHTML();
			converter.TabSize = tabSize;
			converter.Style = style;
            converter.RenderFile(inputFilePath, outputFilePath);
		}
    }

    #endregion
}